<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/5/14 0014
 * Time: 下午 3:35
 */

namespace App\Common;


use App\Enum\SanggEnum;
use App\Enum\ZjhEnum;
use App\Utility\RedisTools;
use App\Utility\BaseEnum;
use App\GameService\games\PlayHelp;
use App\Enum\MahjEnum;
use App\Enum\CardEnum;
use App\GameService\games\PlayTwoEight;
use App\GameService\games\PlayBaiRen;
use App\Model\GameType;
use App\GameService\games\PlayGrabNine;
use App\Utility\Pool\MySQLPool;
use App\Utility\Pool\MySQLObject;
use App\Enum\SysEnum;
use EasySwoole\EasySwoole\Config;
use App\Utility\DBTools;
use EasySwoole\EasySwoole\Task\TaskManager;

/**
 * 公共函数
 * @author XCH
 *
 */
class BaseComm
{
    protected static $win_water_dott = 0.05;  //抽水只针对最后的赢钱的玩家；
    
    //用户状态
    const USER_STATUS = [0=>'禁用',1=>'正常',2=>'锁定',-1=>'删除'];
    const USERLOGS_TYPES = [0=>'抢庄',1=>'下注',2=>'跟注'];          //记录玩家操作日志类型
    /**
     * 构造函数
     * @param array $options 参数
     * @access public
     */
    public function __construct($options = [])
    {
    }

    /**
     * TODO 获取当前毫秒数
     * @return float
     */
    public static function getMillisecond() {
        list($s1, $s2) = explode(' ', microtime());
        return (float)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
    }
    /**
     * TODO 获取返水比值
     * @return number
     */
    public static function getSetWaterDott(){
        return self::$win_water_dott;
    }
    
    /**
     * TODO 当前IP地址；内容
     */
    public static function getRealClientIP(){
        $realip = '';
        if(isset($_SERVER)){
            if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
                $realip=$_SERVER['HTTP_X_FORWARDED_FOR'];
                
            }else if(isset($_SERVER['HTTP_CLIENT_IP'])){
                $realip=$_SERVER['HTTP_CLIENT_IP'];
            }else if(isset($_SERVER['REMOTE_ADDR'])){
                $realip=$_SERVER['REMOTE_ADDR'];
            } else {
                $realip = '';
            }
        }else{
            if(getenv('HTTP_X_FORWARDED_FOR')){
                $realip=getenv('HTTP_X_FORWARDED_FOR');
            }else if(getenv('HTTP_CLIENT_IP')){
                $realip=getenv('HTTP_CLIENT_IP');
            }else{
                $realip=getenv('REMOTE_ADDR');
            }
        }
        return $realip;
    }
    
    /**
     * TODO 取TOKEN信息
     * @param int $userId 用户ID 
     * @return string Token值； 
     */
    public static function createToken($userId){
        $singleToken = '';
        if (empty($userId)) return $singleToken;
        $ip_addr = self::getRealClientIP(); //获取本地IP地址
        $singleToken = md5($userId.$ip_addr.time());
        return $singleToken;
    }
    
    /**
     * TODO 获取当前站点的ID值
     * @return number 当前ID
     */
    public static function getSiteId(){
        $serverID = Config::getInstance()->getConf('COMMON.SiteID');    //获取负载服务器ID
        if (!$serverID) {
            $serverID = 100; //默认的站ID
        }
        return $serverID;
    }
    
    /**
     * TODO 获取站点的访问URL地址
     */
    public static function getSiteReqURL(){
        $lastRURL = "";
        $sitId = self::getSiteId();
        if (!empty($sitId) && intval($sitId)>0) {
            $whereOpts = array( 'siteId'=> ['text' => $sitId,"mark"=> '=']);
            $oneobj = DBTools::PoolGetOne('qc_game_site',["siteId","siteURL"] ,$whereOpts);
            $lastRURL = $oneobj["siteURL"] ?? "";
        }   return $lastRURL;
    }
    
    
    
    /**
     * TODO DB闭包处理
     * @param object $callback
     */
    public static function DbQuery(callable $callback) {
        return MySQLPool::invoke(function(MySQLObject $db) use($callback) {
            return call_user_func($callback,$db);
        });
    }
    
    
    /**
     * TODO 游戏在线人数更新房间； 
     * @param int $gameId
     * @param number $changeNum
     * @param number $type
     * @return boolean
     */
    public static function changeOnLines($gameId,$changeNum = 0, $type = 0){
        $totalNumValue = ($type===1) ? 0 : $changeNum;
        $roomkey = SysEnum::ONLINE_ROOMS_PEPNUMS_KEY;
        
        //TODO 游戏房间内的人数统计更新（REDIS缓存起来）
        $gameNums = RedisTools::Pool_hGet($roomkey, $gameId);           //原有数据为
        if (isset($gameNums) && intval($gameNums) > 0) {
            //Redis更新： 扣除/增加在线人数
            if ($type === 1) {
                $totalNumValue = ($gameNums > $changeNum) ? $gameNums - $changeNum : 0 ;
            }else{
                $totalNumValue = $changeNum + intval($gameNums) ;
            }
        }
        RedisTools::Pool_hSet($roomkey, $gameId , $totalNumValue);    //在线玩家数据更新；
        return TRUE;
    }
    
    /**
     * TODO  获取所有游戏房间的在线人数
     * @return mixed 所有玩家的数据值
     */     
    public static function getRoomOnlines(){
        $roomkey = SysEnum::ONLINE_ROOMS_PEPNUMS_KEY;
        $allNums = RedisTools::Pool_hGetAll($roomkey);           //原有数据为
        return $allNums;
    }
    
    
    /**
     * TODO 进出大厅的玩家FD操作；
     * TO   $typeIn 默认为true进入
     */
    public static function doLobbyInOrOut($userId,$loginFd,$typeIn = TRUE){
        $rediskey = SysEnum::ONLINE_Lobby_USERFD_KEY;
        BaseEnum::Logger("Logger:doLobbyInOrOut(uid={$userId},loginFd={$loginFd},typeIn=$typeIn)","user-login");
        if ($typeIn === TRUE) { //进入
            RedisTools::Pool_hSet($rediskey, $userId, $loginFd);                                        //添加到KEY中
        }else{
            RedisTools::Pool_hDel($rediskey, $userId);                                                  //查找到KEY并删除
        }
        return TRUE;
    }
    
    
    
    //TODO 匹配类型游戏判断是否有正在进行中的游戏( 可以根据时间长判断 )
    public static function popGameFinishING($userId, $GameId = FALSE,&$gameIds = FALSE){
        if(!$userId) return FALSE;
        $nowstamp = BaseEnum::getNowTime(FALSE);
        $gameChar = RedisTools::Pool_hGet(SysEnum::USER_PLAY_FDMAP_KEY, $userId);                       //玩家最新游戏ID对应的号
        list($gameNo,$roomId) = !empty($gameChar) ? json_decode($gameChar,TRUE) : FALSE;                    //取新的房间ID和局号
        if(isset($gameNo) && !empty($gameNo) && intval($userId) > 0){
            $gamekey = SysEnum::ONLINE_ROOMS_LIST_KEY . $gameNo;
            $gamedataStr = RedisTools::Pool_get($gamekey);
            $gamedata = !empty($gamedataStr) ? json_decode($gamedataStr,true):false ;
            $gameId = isset($gamedata['gameId']) ? intval($gamedata['gameId']) : 0;
            $roomId = isset($gamedata['grid']) ? intval($gamedata['grid']) : $roomId;
            $gameStart = isset($gamedata['create']) ? intval($gamedata['create']) : 0 ;
            if(!$gamedata || $gameId <= 0) return FALSE;
            
            //处理逻辑
            if ($gameId === 105) {
                return FALSE;
            }
            BaseEnum::Logger("游戏局号：{$gameNo},游戏数据：".json_encode($gamedata));
            //取时间（200秒以上的游戏都是已过时的）
            if ($gameStart > 0 && $nowstamp - $gameStart > 200) {
                RedisTools::Pool_hDel(SysEnum::USER_PLAY_FDMAP_KEY, $userId);  //清掉KEY
                return FALSE;
            }
            
            $gameIds = [
                "gameId"=> $gameId,"gameRoomId"=> $roomId,"users"=> $gamedata['lists'] ?? FALSE
            ];
            //正在游戏中不能再次匹配
            return TRUE;
        }
        return FALSE;
    }
    
    
    
    /**
     * TODO 获取玩家ID最后玩的游戏局号
     * @param int $userId
     * @param boolean $edit 默认为false,获取数据
     * @param boolean $saveChar 修改内容
     * @return boolean|mixed  数组；
     */
    public static function distLastGameNoRedis($userId,$edit = FALSE, $saveChar = FALSE,$redisobj = FALSE){
        $redis_key = SysEnum::USER_PLAY_FDMAP_KEY;               //取玩家ID对应最后一次游戏数据；Redis
        if ($edit === TRUE) {                                        //更新
            $gameNo = TRUE;
            if($saveChar===FALSE) {
                //$redisobj ->hDel($redis_key, $userId) ;
                RedisTools::Pool_hDel($redis_key, $userId);
            }else{
                //保存数据
                //$redisobj->hSet($redis_key, $userId, $saveChar);    //更新或新加
                RedisTools::Pool_hSet($redis_key, $userId,$saveChar);
            }
        }else{
            $gameNo = RedisTools::Pool_hGet($redis_key, $userId) ;    //玩家最新游戏ID对应的号
        }
        return $gameNo;
    }
    
    
    /**
     * TODO 游戏结束，清理玩家最新期号
     * TO 获取玩家ID最后玩的游戏局号列表
     * @param array $userIds
     * @param boolean $edit 默认为false,获取数据
     * @param boolean $saveChar 修改内容
     * @return boolean|mixed  数组；
     */
    public static function distLastGameNoListRedis($userIds,$edit = FALSE, $saveChar = FALSE){
        $redis_key = SysEnum::USER_PLAY_FDMAP_KEY;               //取玩家ID对应最后一次游戏数据；Redis
        $reslist = [];
        if(isset($userIds) && is_array($userIds) && !empty($userIds)){
            foreach ($userIds as $uid){
                if ($edit === TRUE) {                                       //更新
                    if($saveChar===FALSE) {
                        RedisTools::Pool_hDel($redis_key, $uid);
                    }else{
                        RedisTools::Pool_hSet($redis_key, $uid, $saveChar); //更新或新加
                    }
                }else{
                    $res =  RedisTools::Pool_hGet($redis_key, $uid);        //玩家最新游戏ID对应的号
                    if(!empty($res)){
                        $reslist[$uid] = $res;
                    }
                }
            }
        }
        return $reslist;
    }
    
    /**
     *  TODO 获取游戏开局号；
     * @param number $roomid 房间ID
     * @return string 游戏订单号： 062036007452964330-255
     */
    public static function getRoomUnioxNo($roomid = 0,$lastChar = FALSE){
        if(isset($lastChar) && !empty($lastChar) && strlen($lastChar) == 11){            
            return date('mdHis').$lastChar;  //二八杠取末尾10位值；
        }
        return date('mdHis')."0".substr("{$roomid}", -2).rand(1000,9999)."-".rand(100,299); //总共21位订单号
    }
    
    
    /**
     * TODO 操作内存玩家的数据； 默认获取； $edit=true,为更新，
     * @param int $roomId 房间ID
     * @param bool $edit 默认为false,获取房间$roomId下排队的人数
     * @param array $saveRes 当$edit=true时保存数据 ，$saveRes 为FALSE时，清空数据；
     * @param array $redisMain 对于105这一类游戏以REDIS为主引擎数据内存数据从REDIS取出；
     */
    public static function distMemoryPlayer($roomId,$editFlag = FALSE,$saveRes = FALSE,$redisMain = FALSE) {
        $playUser = FALSE ;     //修改；
        $joinList = !empty($saveRes) ? $saveRes : FALSE ;
        $temp_rediskey = SysEnum::BASEDB_GAMERID_USERS_KEY;     //临时位置
        $redis_main_key = SysEnum::BASEDB_GAMEPLAYING_KEY;      //完整的玩家列表数据
        
        //TODO 特殊情况处理
        if($redisMain === TRUE){
            
            //TODO 存REDIS 
            if($editFlag === TRUE){
                if (!empty($joinList)) {
                    RedisTools::Pool_hSet($redis_main_key, $roomId, BaseEnum::JsonEncode($joinList));           //以JSON保存数据格式
                }else{
                    RedisTools::Pool_hDel($redis_main_key, $roomId);           //清空
                }
                $playUser = TRUE;
            }else{
                $playJson = RedisTools::Pool_hGet($redis_main_key, $roomId);            //取玩家数据
                $playUser = (!empty($playJson) && $playJson != '-1') ? json_decode($playJson,TRUE) : FALSE ; //返回数据格式是Array
            }
        }else{
            //TODO 内存变量中
            if($editFlag === TRUE){
                $joinString = !empty($joinList) ? BaseEnum::JsonEncode($joinList) : -1;
                PlayHelp::getInstance() -> setRoomPlayers($roomId,$joinList);           //玩家信息录入
                BaseEnum::wirteRedisData($temp_rediskey ,$joinString, $roomId);         //内存数据缓存写入Redis；
                $playUser = TRUE;
            }else{
                $playUser = PlayHelp::getInstance() -> getRoomPlayers($roomId);           //匹配玩家列表；
            }
        }
        return $playUser;
    }
    
    
    /**
     * 操作内存游戏的数据； 默认获取； $edit=true,为更新，
     * @param int $gameNo 游戏局号
     * @param bool $edit 默认为false,获取$gameNo下游戏数据
     * @param array $saveRes 当$edit=true时保存数据 ，$saveRes 为FALSE时，清空数据；
     */
    public static function distMemoryRooms($gameNo,$edit=FALSE,$saveRes = FALSE) {
        $list = FALSE ;     //修改；
        if($edit === TRUE){
            $joinList = !empty($saveRes) ? $saveRes : FALSE ;
            PlayHelp::getInstance() -> setGameRoomsData($gameNo,$joinList);                             //游戏信息录入
            $pdata = !empty($joinList) ? json_encode($joinList) : "";
            BaseEnum::wirteRedisData(SysEnum::ONLINE_ROOMS_LIST_KEY . $gameNo, $pdata,FALSE,300 );     //内存数据缓存写入Redis
            $list = TRUE;
        }else{
            $list = PlayHelp::getInstance() -> getGameRoomsData($gameNo);             //匹配玩家列表；
        }
        return $list;
    }
    
    
    /**
     * TODO 操作内存游戏的数据； 默认获取； $edit=true,为更新，
     * @param int $gameNo 游戏局号
     * @param bool $edit 默认为false,获取$gameNo下游戏数据
     * @param array $saveRes 当$edit=true时保存数据 ，$saveRes 为FALSE时，清空数据；
     */
    public static function distMemoryCards($gameNo,$edit=FALSE,$saveRes = FALSE) {
        $list = FALSE ;     //修改；
        $rediskey = 'QPGAME:BaseDB:GameCards';
        if($edit === TRUE){
            $joinList = !empty($saveRes) ? $saveRes : FALSE ;
            PlayHelp::getInstance() -> setRoomCards($gameNo,$joinList);                 //玩家信息录入
            $pdata = !empty($joinList) ? BaseEnum::JsonEncode($joinList) : -1;
            BaseEnum::wirteRedisData($rediskey ,$pdata, $gameNo);              //内存数据缓存写入Redis；
            $list = TRUE;
        }else{
            $list = PlayHelp::getInstance() -> getRoomCards($gameNo);                   //匹配玩家列表；
        }
        return $list;
    }

    /**
     * TODO 操作里玩家虚拟位金额的数据； 默认获取； $edit=true,为更新
     * @param int $userId
     * @param boolean $edit
     * @param boolean $saveRes
     */
    public static function distMemoryPlayMoney($userId,$edit=FALSE,$saveRes = FALSE){
        $list = FALSE ;     //修改；
        if($edit === TRUE){
            $joinList = !empty($saveRes) ? $saveRes : FALSE ;
            PlayHelp::getInstance() -> setPlayMoney($userId,$joinList);                 //玩家信息录入
            $pdata = !empty($joinList) ? BaseEnum::JsonEncode($joinList) : -1;
            BaseEnum::wirteRedisData(SysEnum::BASEDB_GAMEROOM_MONEY_KEY ,$pdata, $userId);              //内存数据缓存写入Redis；
            $list = TRUE;
        }else{
            $list = PlayHelp::getInstance() -> getPlayMoney($userId);                   //匹配玩家列表；
        }
        return $list;
    }
    
    
    /**
     * TODO 操作房间列表页玩家FD用户的存入和取消
     * @param int $userId
     * @param boolean $edit
     * @param boolean $saveRes
     */
    public static function distMemoryFjListUser($gameId,$edit = FALSE,$saveRes = FALSE){
        $list = FALSE ; 
        if($edit === TRUE){
            $list = TRUE;
            $fjUserFds = !empty($saveRes) ? $saveRes : FALSE ;
            PlayHelp::getInstance()->setResultLog($gameId,"fjList",$fjUserFds);                 //列表FD总列表值
            $pdata = !empty($fjUserFds) ? BaseEnum::JsonEncode($fjUserFds) : -1;
            BaseEnum::wirteRedisData(SysEnum::BASEDB_GAMEROOM_USERFDS_KEY ,$pdata, $gameId);                //内存数据缓存写入Redis；
        }else{
            $list = PlayHelp::getInstance() -> getResultLog($gameId,"fjList");                  //匹配玩家列表；
        }
        return $list;
    }
    
    /**
     * 通用创建P牌类型的数据
     * @param string $gameUtype    大类
     * @param boolean $commonParams 通用数据
     * @return Array;
     */
    public static function createCards($gameUtype,$commonParams = false){
        $cardList=false ;
        $playerNum = isset($commonParams['nums']) ? intval($commonParams['nums']) : 0;
        switch ($gameUtype){
            case 'QZNN':
            case 'BRNN':
            case 'LOOKQZNN':
                $cardList = CardEnum::createRoomGameQZNN($playerNum,$commonParams,FALSE);     //5张牌；
                break;
            case 'TWOEBAR':
                $cardList = MahjEnum::createRoomGameTWOEBAR($playerNum,$commonParams,FALSE);  //两张牌；
                break;
            case 'QZGRAB':
                $cardList = MahjEnum::createRoomGameQZGRAB($playerNum,$commonParams,FALSE);  //两张牌；
                break;
            case 'DZPK':
                $cardList = CardEnum::createRoomGameDZPK($playerNum,$commonParams,FALSE);     //两张手牌和五张公共牌
                break;
            case 'SANGG':
                $cardList = SanggEnum::createRoomGameSANGG($playerNum,$commonParams,FALSE);
                break;
            case 'ZJH':
                $cardList = ZjhEnum::createRoomGameZJH($playerNum,$commonParams,FALSE);
                break;
        }
        return $cardList;
    }
    
    /**
     * 通用结算指示
     * @param string $gameUtype
     * @param boolean $commonParams
     * @return boolean
     */
    public static function dealCards($gameUtype,$zjObject=false,$useCards=false,$commonParam = false,& $returndata=false){
        $lastList=false ;
        switch ($gameUtype){
            case 'QZNN':
            case 'LOOKQZNN':
                $lastList = CardEnum::dealRoomGameQZNN($zjObject,$useCards,$commonParam,$returndata); //5张牌；
                break;
            case 'TWOEBAR':
                $lastList = PlayTwoEight::dealRoomGameTWOEBAR($zjObject,$useCards,$commonParam,$returndata); //2张牌；
                break;
            case 'QZGRAB':
                $lastList = PlayGrabNine::dealRoomGameQZGRAB($zjObject,$useCards,$commonParam,$returndata); //2张牌；
                break;
            case 'BRNN':
                $lastList = PlayBaiRen::dealRoomGameBRNN($zjObject,$useCards,$commonParam,$returndata); //5张牌；
                break;
            case 'SANGG':
                $lastList = SanggEnum::dealRoomGameSANGG($zjObject,$useCards,$commonParam,$returndata);//已经将牌倍数传递过来
                break;
        }
        return $lastList;
    }
    
    
    /**
     * 组装玩家的牌数据；
     * @param string $gameUtype
     * @param boolean $commonParams
     * @return boolean
     */
    public static function conectUserCardsValue($gameUtype,$maxNum,$allCards = false,$zjSeatNo = -1){
        $cardValue=false;
        $cards_num = 5; 
        switch ($gameUtype){
            case 'QZNN':
            case 'LOOKQZNN':
            case 'BRNN':
                $cards_num = 5;
                break;
            case 'TWOEBAR':
            case 'QZGRAB':
                $cards_num = 2;
                break;
            case 'SANGG':
                $cards_num = 3;
                break;
        }
        if($cards_num > 0){
            //每个人5张牌； 最多5个位置  0000000000 0000000000 0000000000 0000000000 X
            $frontChar = [];
            for ($i = 0; $i < $maxNum; $i++) {
                $onecard = isset($allCards[$i]) ? $allCards[$i] : [];
                if(!$onecard){
                    $onecard = [];
                    for ($j = 0; $j < $cards_num; $j++) { $onecard[$j] = 0; }
                }
                foreach ($onecard as & $onechar){ $onechar = str_pad($onechar,2,"0",STR_PAD_LEFT); }     //添满两个字符占位；
                $frontChar[] = implode("", $onecard);       //组装手牌
            }
            if(!empty($frontChar)){ $frontChar[] = $zjSeatNo ; }
            $cardValue = !empty($frontChar) ? implode('.', $frontChar) : FALSE;
        }
        return $cardValue;
    }
    
    /**
     * 所有玩家都匿名传入操作
     * @param string $userName
     */
    public static function hiddenUserName($userName) {
        $hiddenName = $userName;
        if(!empty($userName) && preg_match("/^[\x{4e00}-\x{9fa5}]+$/u", $userName)){    //中文都不操作；
            $hiddenName = $userName;
        }else if(!empty($userName) && strlen($userName) >= 4){
            $hiddenName = "****".substr($userName, -3);
        }elseif(strlen($hiddenName) <= 4){
            $hiddenName = "****".$userName;
        }
        return $hiddenName;
    }
    
    
    /**
     * 获取房间信息
     * @param int $gameRid
     * @return boolean
     */
    public static function GameRoomDetail($gameRid) {
        if(!$gameRid)return false ;
        $gameModel = new GameType();
        return $gameModel->getGameRoomDetail($gameRid);
    }

    
    /**
     * TODO 写入游戏对局日志中
     * @param string $gameNo    游戏编号
     * @param string $logtext   日志详细
     * @param boolean $param    其它参数
     */
    public static function gameLogger($gameNo,$logtext = FALSE,$param = FALSE){
        TaskManager::getInstance()->async(function () use($gameNo,$logtext,$param){
            $redisKey = SysEnum::GAME_LOGS_KEY;
            if (!$gameNo || empty($logtext))return FALSE;
            $content =  RedisTools::Pool_hGet($redisKey, $gameNo);
            $Logg = !empty($content) ? unserialize($content) : FALSE;
            $gameINo = $Logg['gameNo'] ?? FALSE;
            $logglist = $Logg['logs'] ?? [];
            if (!empty($Logg) && !empty($gameINo) && $gameINo == $gameNo && !empty($logglist)) {
                //TODO 追加一条日志进入列表中
                if(is_array($logtext)){
                    $logglist = array_merge($logglist,$logtext);
                }else{
                    @array_push($logglist, $logtext);
                }
               $Logg['logs'] = $logglist;
               if (isset($param['finish']) && $param['finish'] === TRUE) {
                   $Logg['finish'] = TRUE;  //TODO 标识已经添加结束，就要入库操作
                   if(!empty($Logg) && $gameINo){
                       $data = [
                           'gameNo' => $Logg['gameNo'],
                           'logs' => !empty($logglist) ? implode(PHP_EOL,$Logg['logs']) : '',
                           'created' => $Logg['created'] ?? time()
                       ];
                       DBTools::PoolInsert('qc_game_logs',$data);
                       RedisTools::Pool_hDel($redisKey,$gameNo);
                       return true;  //结算已经入库不必写redis
                   }
               }
            }else{
                $logs2 = is_string($logtext) ? [$logtext] : $logtext;
                $Logg = ["gameNo"=>$gameNo,"logs"=>$logs2,"finish"=> FALSE,'created'=> time()];
            }
            RedisTools::Pool_hSet($redisKey, $gameNo, serialize($Logg)) ;       //添加到 [对局日志]//
            return true;
        });
    }
    
    
    /**
     * TODO 写游戏日志到缓存REDIS中
     * @param $user ['uid'=>10001,'username'=>'test01']
     * @param $typeId
     * @param $info
     * @param bool $otherName '下注'
     * @return bool
     */
    public static function userLogger($user,$typeId,$info,$otherName=false){
        if (!isset($user['uid']) || empty($user['uid']) || !isset($user['username']) || empty($user['username'])){
            return false;
        }
        $type = self::USERLOGS_TYPES;
        $tName = '下注';
        if (isset($otherName) && !empty($otherName)){
            $tName = $otherName;
        }else if(isset($type[$typeId])){
            $tName = $type[$typeId];
        }
        $data = [
            'uid' => $user['uid'],
            'username' => $user['username'],
            'type' => $typeId,
            'tName' => $tName,
            'ip' => self::getRealClientIP(),
            'info' => $info,
            'created' => time(),
        ];
        $res = RedisTools::Pool_hSet(SysEnum::USERLOGS_KEY,self::getMillisecond(),json_encode($data,JSON_UNESCAPED_UNICODE));
        if($res){
            return true;
        }
        return false;
        
    }


    
    /**
     * TODO 多进程http:请求
     * @param array $linkUrls
     * @param number $timeout
     */
    public static function doMoreRequest($linkUrls, $timeOut = 10)
    {
        if (! is_array($linkUrls) || sizeof($linkUrls) == 0)
            return false;
            $timeOut = $timeOut > 0 ? $timeOut : 10;
            $handleList = @curl_multi_init();           // 多线程容器
            $msgs_in_queue = 0;
            $handles = array();
            $curResult = array();
            foreach ($linkUrls as $k => $firsturl) {    // 创建句柄
                $oneCurl = @curl_init(); // 创建对象
                @curl_setopt($oneCurl, CURLOPT_URL, $firsturl); // 不转换 rawurlencode($firsturl)
                @curl_setopt($oneCurl, CURLOPT_REFERER, '');
                @curl_setopt($oneCurl, CURLOPT_ENCODING, "gzip, deflate, sdch");
                @curl_setopt($oneCurl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0');
                @curl_setopt($oneCurl, CURLOPT_FOLLOWLOCATION, true);
                @curl_setopt($oneCurl, CURLOPT_TIMEOUT, $timeOut);
                @curl_setopt($oneCurl, CURLOPT_RETURNTRANSFER, 1); // 302redirect
                // 添加到多线程列表中去
                @curl_multi_add_handle($handleList, $oneCurl);
                $msgs_in_queue ++;
                $curResult[$k] = $oneCurl;
                $handles["{$k}"] = $firsturl;
                unset($oneCurl);
            }
            $running = null;
            //$logpath = public_path("/syslog/error-" . date('Ymd') . ".log");
            $runningRes = false;
            $lastRes = array(); // curl数组用来记录各个curl句柄的返回值
            if (! empty($curResult)) {
                do {
                    $runningRes = @curl_multi_exec($handleList, $running); // CURL执行
                } while ($running > 0);
                // 再检查一次
                while ($running && $runningRes == CURLM_OK) {
                    if (curl_multi_select($handleList) != - 1) {
                        do {
                            $runningRes = @curl_multi_exec($handleList, $running);
                        } while ($runningRes == CURLM_CALL_MULTI_PERFORM);
                    }
                }
                // 结束处理；获取结果
                foreach ($curResult as $kl => $v) {
                    $currCurobj = isset($curResult[$kl]) ? $curResult[$kl] : false; // 当前句柄
                    if ($currCurobj) {
                        // 循环读取curl返回，并根据其句柄对应的key一起记录到$curls数组中,保证返回的数据不乱序；
                        $newRestod = array(
                            "so" => $currCurobj,
                            'url' => isset($handles[$kl]) ? $handles[$kl] : ''
                        );
                        if (curl_error($currCurobj) == "") {
                            $newRestod["content"] = (string) @curl_multi_getcontent($currCurobj);
                        } else {
                            $newRestod["content"] = "req失败:" . curl_error($currCurobj);
                        }
                        $lastRes[$kl] = $newRestod;
                        @curl_multi_remove_handle($handleList, $currCurobj);
                        @curl_close($currCurobj);
                    } else {
                        // self::LogInfoFile("GameNotrr ======= Request_Error:K=={$kl} is NULL !!!!!===>".var_dump($currCurobj,true),'request222');
                    }
                }
                // self::LogInfoFile("GameNotrr ======= Request_Success:".print_r($lastRes,true),'request222');
            }
            @curl_multi_close($handleList); // 关闭curl_multi句柄
            return $lastRes;
    }
    
    

}